package org.gaaidou.ptarmigan.springboot.distributed.zk;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.gaaidou.ptarmigan.springboot.distributed.exception.DistributedLockException;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class ZkLock implements Lock {


    private final int lockTimeoutSeconds;

    private final InterProcessMutex lock;


    private static final int INTERVAL_MILLISECONDS = 50;

    ZkLock(String resource, int lockTimeoutSeconds, CuratorFramework client) {
        this.lockTimeoutSeconds = lockTimeoutSeconds;
        this.lock = new InterProcessMutex(client, String.format("/lock/%s", resource));
    }

    @Override
    public void lock() {
        try {
            boolean lock = tryLock(lockTimeoutSeconds, TimeUnit.SECONDS);
            Assert.isTrue(lock, "Distributed lock error!");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }


    @Override
    public synchronized void lockInterruptibly() throws InterruptedException {
        while (true) {
            if (this.tryLock()) {
                return;
            }
            TimeUnit.MILLISECONDS.sleep(INTERVAL_MILLISECONDS);
        }
    }


    @Override
    public synchronized boolean tryLock() {
        try {
            return lock.acquire(lockTimeoutSeconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new DistributedLockException(e);
        }
    }

    @Override
    public synchronized boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        int size = (int) (unit.toMillis(time) / INTERVAL_MILLISECONDS);
        for (int index = 0; index <= size; index++) {
            if (this.tryLock()) {
                return true;
            }
            TimeUnit.MILLISECONDS.sleep(INTERVAL_MILLISECONDS);
        }
        return false;
    }


    @Override
    public void unlock() {
        try {
            lock.release();
        } catch (Exception e) {
            throw new DistributedLockException(e);
        }
    }

    @Override
    public Condition newCondition() {
        throw new IllegalArgumentException("Not support");
    }
}
